Release 10.1A: OpenEdge Development:
Messaging and ESB


Publishing, subscribing, and receiving an XML document in a BytesMessage

The procedures example12.p and example13.p (1 of 2) use a MEMPTR variable to publish and receive an XML document in a BytesMessage to prevent code-page conversions. The code pages of the document and the 4GL client do not have to match.

To run example12.p and example13.p:

  1. Run example13.p (1 of 2) to subscribe and receive a BytesMessage containing an XML document, as shown:
  2. example13.p
    /* Receives an XML document in a Bytes message. */ 
    DEFINE VARIABLE pubsubsession AS HANDLE. 
    DEFINE VARIABLE msgConsumer1 AS HANDLE. 
    DEFINE VARIABLE stillWaiting AS LOGICAL INIT yes. 
    RUN jms/pubsubsession.p PERSISTENT SET pubsubsession  
                                      ("-H localhost -S 5162 "). 
    RUN setBrokerURL IN pubsubsession ("localhost:2506"). 
    RUN beginSession IN pubsubsession. 
    RUN createMessageConsumer IN pubsubsession(THIS-PROCEDURE, 
      "messageHandler", 
      OUTPUT msgConsumer1). 
    RUN subscribe IN pubsubsession ( 
                       "xmlTopic", 
                       ?,          /* Not a durable subscription */ 
                       ?,          /* No message selector. */ 
                       no,         /* noLocal */ 
                       msgConsumer1) NO-ERROR. 
    RUN startReceiveMessages IN pubsubsession. 
    RUN waitForMessages IN pubsubsession ("inWait", THIS-PROCEDURE, ?). 
    RUN deleteSession IN pubsubsession. 
    
    PROCEDURE messageHandler: 
    DEFINE INPUT PARAMETER messageH AS HANDLE NO-UNDO. 
    DEFINE INPUT PARAMETER messageConsumerH AS HANDLE NO-UNDO. 
    DEFINE OUTPUT PARAMETER replyH AS HANDLE NO-UNDO. 
    DEFINE VAR memptrDoc AS MEMPTR.  
    DEFINE VAR hdoc AS HANDLE. 
    DEFINE VAR hRoot AS HANDLE. 
        memptrDoc = DYNAMIC-FUNCTION('getMemptr':U IN messageH). 
        CREATE X-DOCUMENT hdoc. 
        CREATE X-NODEREF hRoot. 
        hdoc:LOAD("memptr", memptrDoc, FALSE). 
        hdoc:GET-DOCUMENT-ELEMENT(hRoot). 
        RUN GetChildren(hRoot, 1). 
        RUN deleteMessage IN messageH. 
        stillWaiting = false. 
    END. 
    
    PROCEDURE GetChildren: 
    DEFINE INPUT PARAMETER hParent AS HANDLE. 
    DEFINE INPUT PARAMETER level AS INT. 
    DEFINE VAR i AS INT. 
    DEFINE VAR hNoderef AS HANDLE. 
        CREATE X-NODEREF hNoderef. 
        REPEAT i = 1 TO hParent:NUM-CHILDREN. 
              hParent:GET-CHILD(hNoderef,i). 
              IF hNoderef:NAME = "#text" THEN 
                 MESSAGE "Node text: " hNoderef:NODE-VALUE. 
              ELSE 
                 MESSAGE "Node name: " hNoderef:NAME. 
              RUN GetChildren(hNoderef, (level + 1)). 
        END. 
        DELETE OBJECT hNoderef. 
    END. 
    
    FUNCTION inWait RETURNS LOGICAL. 
        RETURN stillWaiting. 
    END. 
    

  3. Run example12.p to publish the BytesMessage containing an XML document, as shown:
  4. example12.p
    /* Publishes an XML document in a Bytes message. */ 
    DEFINE VARIABLE pubsubsession AS HANDLE. 
    DEFINE VARIABLE mesgH AS HANDLE. 
    DEFINE VARIABLE memVal AS MEMPTR. 
    DEFINE VAR hdoc AS HANDLE. 
    RUN jms/pubsubsession.p PERSISTENT SET pubsubsession  
                                      ("-H localhost -S 5162 "). 
    RUN setBrokerURL IN pubsubsession ("localhost:2506"). 
    RUN beginSession IN pubsubsession. 
    RUN createBytesMessage IN pubsubsession (OUTPUT mesgH). 
    CREATE X-DOCUMENT hdoc. 
    hdoc:LOAD("file", "personal.xml", FALSE). 
    hdoc:SAVE("memptr", memVal). 
    RUN setMemptr IN mesgH(memVal, ?, ?). 
    RUN publish IN pubsubsession ("xmlTopic", mesgH, ?, ?, ?). 
    SET-SIZE(memVal) = 0. 
    RUN deleteMessage IN mesgH. 
    RUN deleteSession IN pubsubsession. 
    
    /* 
     The personal.xml document: 
    <?xml version="1.0" encoding='UTF-8' ?> 
    <personnel> 
      <person id="Irving.Nigrini"> 
        <name><family>Nigrini</family> <given>Irving</given></name> 
        <email>inigrini@subpargolf.com</email> 
        <link manager="Thomas.Roy"/> 
      </person> 
      <person id="Jules.Nigrini"> 
        <name><family>Nigrini</family> <given>Jules</given></name> 
        <email>jnigrini@subpargolf.com</email> 
        <link manager="Thomas.Roy"/> 
      </person> 
    </personnel> 
    */ 
    

XML code page encoding

4GL applications work with the built-in XML parser. It is important to consider the code page encoding of XML messages. In principle, XML documents can be encoded with any code page. However, XML parsers support some or all code pages, and XML parsers also differ with respect to the code page conversions that they support.

4GL clients set and get XML text using the 4GL CHARACTER data type. CHARACTER data is encoded by the 4GL interpreter according to the internal code page (the -cpinternal startup parameter). The 4GL-JMS implementation automatically converts the text to Unicode when it is sent to the JMS server, and from Unicode to the internal client’s code page when the text is sent from the server to the client.

In general, when the characters used by the XML document are from the 7-byte ASCII subset, there are no issues the 4GL programmer has to consider. Otherwise, observe the following examples and guidelines in the following examples.

Code page example 1

In this example, two 4GL clients use the ISO8859-1 code page:

The following code-page conversions take place:

  1. ISO8859-1 (client1) to Unicode (SonicMQ XMLMessage).
  2. Unicode (SonicMQ XMLMessage) to ISO8859-1 (client2).

In this example, the XML parser parses the XML document correctly if:

  1. The header of the document specifies that the encoding is ISO8859-1.
  2. The parser can handle ISO8859-1.
Code page example 2

In this example, two 4GL clients use ISO8859–1 for their internal code page. Client1 saves a UTF–8 encoded XML document in a MEMPTR variable (calling the X–DOC:SAVE() 4GL method) and then uses the 4GL GET–STRING statement to extract the text from the MEMPTR and pass it into the XMLMessage. (This is a deliberate error.) UTF–8 (Unicode Transformation Format) is an 8-bit encoding form that serializes a Unicode scalar value as a sequence of one to four bytes.

A 4GL client cannot mix code pages. The text it sets in the XMLMessage must be encoded in the same code page as the client’s internal code page. In general, a MEMPTR variable must be used carefully, since it can have any data in it. The 4GL programmer must be sure that it contains only NULL free text (no embedded NULL bytes), encoded with the same code page as the internal code page, before loading it into an XMLMessage.

In this example, if the 4GL client cannot be started up with –cpinternal UTF–8, but still wants to use 4GL-JMS to pass that UTF–8 document, it can use a BytesMessage or bytes elements in a StreamMessage. When sent as bytes, the XML data will get to the receiver uninterpreted and unconverted. The 4GL receiver can then set the data in a MEMPTR variable and load the parser.

A second option is to convert the text (and the document’s header) to ISO8859–1 using the CODEPAGE–CONVERT 4GL function. However, if -cpinternal represents all character, the conversion is automatic if you use LONGCHAR or CHAR. If -cpinternal represents all characters, the conversion is also automatic when you use the new built-in XML routines (SAX-WRITER or setX-Document). When you use the new built-in XML routines, you can create, send, and receive UTF-8 XML documents.

If the 4GL receiver of an XMLMessage is unsure about the XML header encoding declaration, it must check it and perhaps modify it to match its internal code page before loading the parser.


Copyright © 2005 Progress Software Corporation
www.progress.com
Voice: (781) 280-4000
Fax: (781) 280-4095